package com.ruoyi.plus.utils.minio;


import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * oss对象存储服务minio工具类
 *
 * @author likun
 */
@Slf4j
@Component
public class MinIoUtils {

    public static MinioClient minioClient;
    public static MinIoUtils minIoUtils;
    public static MinioProperties config;

    @Autowired
    private MinioProperties minioProperties;


    /**
     * 文件上传 （返回URL下载地址）
     *
     * @param file 文件
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String uploadFile(MultipartFile file) {
        final String fileName = FileUploadUtils.extractFilename(file);
        return uploadFile(file, fileName);
    }

    /**
     * 文件上传 （返回URL下载地址）
     *
     * @param file 文件
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String uploadFile(MultipartFile file, String fileName) {
        fileName = filterFileName(fileName);
        final InputStream is = file.getInputStream();
        String contentType = file.getContentType();
        if (contentType == null || contentType.isEmpty()) {
            contentType = "*/*";
        }
        minioClient.putObject(PutObjectArgs.builder().bucket(config.getBucketName())
                .object(fileName).stream(is, is.available(), -1)
                .contentType(contentType)
                .build());
        is.close();
        return getFileUrl(config.getBucketName(), fileName);
    }

    /**
     * 文件上传 （返回URL下载地址）
     *
     * @param stream   文件
     * @param fileName 文件名(fileName需带后缀，minio根据后缀推断类型)
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String uploadStream(InputStream stream, String fileName) {
        fileName = filterFileName(fileName);
        String contentType = "*/*";
        minioClient.putObject(PutObjectArgs.builder().bucket(config.getBucketName())
                .object(fileName).stream(stream, stream.available(), -1)
                .contentType(contentType)
                .build());
        stream.close();
        return getFileUrl(config.getBucketName(), fileName);
    }

    /**
     * 文件上传 （返回URL下载地址）
     *
     * @param stream      文件
     * @param fileName    文件名(fileName需带后缀)
     * @param contentType 文件类型
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String uploadStream(InputStream stream, String fileName, String contentType) {
        fileName = filterFileName(fileName);
        if (contentType == null || contentType.isEmpty()) {
            contentType = "*/*";
        }
        minioClient.putObject(PutObjectArgs.builder().bucket(config.getBucketName())
                .object(fileName).stream(stream, stream.available(), -1)
                .contentType(contentType)
                .build());
        stream.close();
        return getFileUrl(config.getBucketName(), fileName);
    }

    @SneakyThrows(Exception.class)
    public static String composeObject(List<ComposeSource> composeSourceList, String fileName) {
        minioClient.composeObject(
                ComposeObjectArgs.builder()
                        .bucket(config.getBucketName())
                        .object(fileName)
                        .sources(composeSourceList)
                        .build());
        return getFileUrl(config.getBucketName(), fileName);
    }

    /**
     * 文件上传 （返回URL下载地址）
     *
     * @param file 文件
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String uploadFile(File file) {
        if (!file.exists()) {
            return null;
        }
        String fileName = DateUtils.datePath() + "/" + file.getName();
        minioClient.uploadObject(UploadObjectArgs.builder()
                .bucket(config.getBucketName()).object(fileName).filename(file.getPath())
                .build());
        return getFileUrl(config.getBucketName(), fileName);
    }

    /**
     * 删除默认桶内的指定文件
     *
     * @param fileName 文件名
     */
    @SneakyThrows(Exception.class)
    public static void deleteFile(String fileName) {
        fileName = filterFileName(fileName);
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(config.getBucketName()).object(fileName).build());
    }


    /**
     * 判断 bucket是否存在
     *
     * @param bucketName 桶名
     * @return: boolean
     */
    @SneakyThrows(Exception.class)
    public static boolean bucketExists(String bucketName) {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建 bucket
     *
     * @param bucketName 桶名
     * @return: void
     */
    @SneakyThrows(Exception.class)
    public static void createBucket(String bucketName) {
        boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!isExist) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 获取全部bucket
     *
     * @return: java.util.List<io.minio.messages.Bucket>
     */
    @SneakyThrows(Exception.class)
    public static List<Bucket> getAllBuckets() {
        return minioClient.listBuckets();
    }


    /**
     * 文件上传（返回URL下载地址）
     *
     * @param bucketName 桶名
     * @param fileName   文件名
     * @param stream     文件流
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String upload(String bucketName, String fileName, InputStream stream) {
        fileName = filterFileName(fileName);
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName).object(fileName).stream(stream, stream.available(), -1).build());
        return getFileUrl(bucketName, fileName);
    }

    /**
     * 文件上传 （返回URL下载地址）
     *
     * @param bucketName 桶名
     * @param file       文件
     * @return: 文件url下载地址
     */
    @SneakyThrows(Exception.class)
    public static String upload(String bucketName, MultipartFile file) {
        final InputStream is = file.getInputStream();
        final String fileName = file.getOriginalFilename();
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName).object(fileName).stream(is, is.available(), -1).build());
        is.close();
        return getFileUrl(bucketName, fileName);
    }


    /**
     * 删除文件
     *
     * @param bucketName 桶名
     * @param fileName   文件名
     */
    @SneakyThrows(Exception.class)
    public static void deleteFile(String bucketName, String fileName) {
        fileName = filterFileName(fileName);
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
    }

    /**
     * 下载文件 （流输出）
     *
     * @param bucketName 桶名
     * @param fileName   文件名
     */
    @SneakyThrows(Exception.class)
    public static void download(String bucketName, String fileName, HttpServletResponse response) {
        fileName = filterFileName(fileName);
        // 获取对象的元数据
        StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
        response.setContentType(stat.contentType());
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        InputStream is = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
        IOUtils.copy(is, response.getOutputStream());
        is.close();
    }

    /**
     * 获取minio文件的下载地址
     *
     * @param bucketName 桶名
     * @param fileName   文件名
     */
    @SneakyThrows(Exception.class)
    public static String getFileUrl(String bucketName, String fileName) {
        //return minioClient.presignedGetObject(bucketName, fileName);
        //return minioClient.getObjectUrl(bucketName, fileName).replace(minioConfig.getApiUrl(), minioConfig.getViewUrl());
        String url = config.getViewUrl() + ("/" + bucketName + "/" + fileName).replace("\\", "/").replace("//", "/");
        log.info(url);
        return url;
    }

    @SneakyThrows(Exception.class)
    public static String filterFileName(String fileName) {
        if (fileName != null) {
            fileName = fileName.replace(config.getViewUrl() + "/" + config.getBucketName() + "/", "");
            if (fileName.toLowerCase().contains("/oss-")) {
                fileName = fileName.substring(fileName.toLowerCase().indexOf("/oss-") + 1);
                fileName = fileName.substring(fileName.indexOf("/") + 1);
            }
            return fileName;
        }
        return "";
    }

    /**
     * 判断文件是否存在
     *
     * @param objectName 文件名
     * @return
     */
    public static boolean isObjectExist(String objectName) {
        boolean exist = true;
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(config.getBucketName()).object(objectName).build());
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param folderName 文件夹名称
     * @return
     */
    public static boolean isFolderExist(String folderName) {
        boolean exist = false;
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(config.getBucketName()).prefix(folderName).recursive(true).maxKeys(2).build());
            if (results != null && results.iterator().hasNext()) {
                return true;
            }
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 根据文件前缀查询文件
     *
     * @param prefix    前缀
     * @param recursive 是否使用递归查询
     * @return MinioItem 列表
     * @throws Exception
     */
    public static List<Item> getAllObjectsByPrefix(String prefix, boolean recursive) throws Exception {
        List<Item> list = new ArrayList<>();
        Iterable<Result<Item>> objectsIterator = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(config.getBucketName()).prefix(prefix).recursive(recursive).build());
        if (objectsIterator != null) {
            for (Result<Item> o : objectsIterator) {
                Item item = o.get();
                list.add(item);
            }
        }
        return list;
    }

    /**
     * 初始化minio配置
     */
    @PostConstruct
    public void init() {
        if (this.minioProperties != null) {
            config = minioProperties;
            if (this.minioProperties.isEnabled()) {
                log.info(">>>>>>>>>>>minio 初始化开始");
                minIoUtils = this;
                minIoUtils.minioProperties = this.minioProperties;
                try {
                    config = minioProperties;
                    minioClient = MinioClient.builder().endpoint(minioProperties.getApiUrl()).credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()).build();
                    createBucket(minioProperties.getBucketName());
                    log.info(">>>>>>>>>>>minio 初始化成功!");
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("》》》》》》》》》》初始化minio异常: 【{}】", e.fillInStackTrace());
                }
            }
        }
    }

}
